﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Mail;

using Xpus.Website.Configuration;

namespace Xpus.Website.Utilities
{
	/// <summary>
	/// 用于提供以 xpus 管理员身份发送电子邮件的功能。
	/// </summary>
	/// <remarks>
	/// xpus 管理员电子邮件在 web.config 中的 xpus/mailServer
	/// 节中进行设置。
	/// </remarks>
	public static class MailUtility
	{
		/// <summary>
		/// 向一个接收者发送邮件。
		/// </summary>
		/// <param name="subject">邮件标题。</param>
		/// <param name="body">邮件内容。</param>
		/// <param name="address">收件人。</param>
		public static void SendMail(
			string subject,
			string body,
			string address)
		{
			SmtpClient smtp = CreateSmtpClient();
			smtp.SendCompleted += SendMailCompleted;

			MailMessage msg = CreateMailMessage(
				subject,
				body,
				address);
			smtp.SendAsync(msg, msg);
		}

		/// <summary>
		/// 向多个接收者发送邮件。
		/// </summary>
		/// <param name="subject">邮件标题。</param>
		/// <param name="body">邮件内容。</param>
		/// <param name="addresses">所有收件人。</param>
		public static void SendMail(
			string subject,
			string body,
			List<string> addresses)
		{
			SmtpClient smtp = CreateSmtpClient();
			smtp.SendCompleted += SendMailCompleted;

			MailMessage msg = CreateMailMessage(
				subject,
				body,
				addresses);
			smtp.SendAsync(msg, msg);
		}

		/// <summary>
		/// 当邮件发送完毕后，用于处理 SendCompleted 事件。
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private static void SendMailCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
		{
			MailMessage msg = e.UserState as MailMessage;
			if(msg != null)
			{
				msg.Dispose();
				msg = null;
			}
		}

		/// <summary>
		/// 根据 web.config 中的配置创建 SMTP 客户端。
		/// </summary>
		/// <returns>新建的 SmtpClient 实例。</returns>
		private static SmtpClient CreateSmtpClient()
		{
			SmtpClient client = new SmtpClient();

			MailServerConfiguration mailConfig = XpusConfigurationSection.Instance.MailServer;

			client.Host = mailConfig.Host;
			if(mailConfig.Port > 0)
				client.Port = mailConfig.Port;
			client.Credentials = new MailServerCredentials(
				mailConfig.UserName, mailConfig.Password);

			return client;
		}

		/// <summary>
		/// 创建邮件消息实例。
		/// </summary>
		/// <param name="subject">主题。</param>
		/// <param name="body">内容。</param>
		/// <param name="address">收件人地址。</param>
		/// <returns>新建的邮件消息实例。</returns>
		private static MailMessage CreateMailMessage(
			string subject,
			string body,
			string address)
		{
			MailMessage msg = new MailMessage(
				XpusConfigurationSection.Instance.MailServer.Address,
				address,
				subject,
				body);

			return msg;
		}

		/// <summary>
		/// 创建邮件消息实例。
		/// </summary>
		/// <param name="subject">主题。</param>
		/// <param name="body">内容。</param>
		/// <param name="addresses">收件人地址。</param>
		/// <returns>新建的邮件消息实例。</returns>
		private static MailMessage CreateMailMessage(
			string subject,
			string body,
			List<string> addresses)
		{
			MailMessage msg = new MailMessage();
			msg.From = new MailAddress(
				XpusConfigurationSection.Instance.MailServer.Address);
			msg.Subject = subject;
			msg.Body = body;

			foreach(string to in addresses)
			{
				msg.To.Add(to);
			}

			return msg;
		}
	}

	/// <summary>
	/// 表示邮件服务器所需的身份验证。
	/// </summary>
	internal class MailServerCredentials : ICredentialsByHost
	{
		string _userName, _password;

		/// <summary>
		/// 根据用户名和密码创建新的身份凭证。
		/// </summary>
		/// <param name="userName">用户名。</param>
		/// <param name="password">密码。</param>
		public MailServerCredentials(string userName, string password)
		{
			_userName = userName;
			_password = password;
		}

		#region ICredentialsByHost 成员

		/// <summary>
		/// 实现 <see cref="ICredentialsByHost"/> 接口。
		/// </summary>
		/// <param name="host"></param>
		/// <param name="port"></param>
		/// <param name="authenticationType"></param>
		/// <returns></returns>
		public NetworkCredential GetCredential(string host, int port, string authenticationType)
		{
			NetworkCredential cred = new NetworkCredential(
				_userName,
				_password);

			return cred;
		}

		#endregion
	}
}
